package concurrency;//: concurrency/NotifyVsNotifyAll.java

import java.util.concurrent.*;
import java.util.*;

class Blocker {
	synchronized void waitingCall() {
		try {
			while (!Thread.interrupted()) {
				wait();
				System.out.print(Thread.currentThread() + " ");
			}
		} catch (InterruptedException e) {
			// OK to exit this way
		}
	}

	synchronized void prod() {
		notify();
	}

	synchronized void prodAll() {
		notifyAll();
	}
}

class Task implements Runnable {
	static Blocker blocker = new Blocker();

	public void run() {
		blocker.waitingCall();
	}
}

class Task2 implements Runnable {
	// A separate Blocker object:
	static Blocker blocker = new Blocker();

	public void run() {
		blocker.waitingCall();
	}
}

public class NotifyVsNotifyAll {
	public static void main(String[] args) throws Exception {
		ExecutorService exec = Executors.newCachedThreadPool();
		for (int i = 0; i < 5; i++)
			exec.execute(new Task());
		exec.execute(new Task2());
		Timer timer = new Timer();
		System.out.println("-------------------");
		timer.scheduleAtFixedRate(new TimerTask() {
			boolean prod = true;

			public void run() {
				if (prod) {
					System.out.print("\nnotify() ");
					Task.blocker.prod();
					prod = false;
				} else {
					System.out.print("\nnotifyAll() ");
					Task.blocker.prodAll();
					prod = true;
				}
			}
		}, 400, 400); // Run every .4 second
		TimeUnit.SECONDS.sleep(5); // Run for a while...
		timer.cancel();
		System.out.println("\nTimer canceled");
		TimeUnit.MILLISECONDS.sleep(500);
		System.out.print("Task2.blocker.prodAll() ");
		Task2.blocker.prodAll();
		TimeUnit.MILLISECONDS.sleep(500);
		System.out.println("\nShutting down");
		exec.shutdownNow(); // Interrupt all tasks
	}
} /*
 * Output: (Sample) notify() Thread[pool-1-thread-1,5,main] notifyAll()
 * Thread[pool-1-thread-1,5,main] Thread[pool-1-thread-5,5,main]
 * Thread[pool-1-thread-4,5,main] Thread[pool-1-thread-3,5,main]
 * Thread[pool-1-thread-2,5,main] notify() Thread[pool-1-thread-1,5,main]
 * notifyAll() Thread[pool-1-thread-1,5,main] Thread[pool-1-thread-2,5,main]
 * Thread[pool-1-thread-3,5,main] Thread[pool-1-thread-4,5,main]
 * Thread[pool-1-thread-5,5,main] notify() Thread[pool-1-thread-1,5,main]
 * notifyAll() Thread[pool-1-thread-1,5,main] Thread[pool-1-thread-5,5,main]
 * Thread[pool-1-thread-4,5,main] Thread[pool-1-thread-3,5,main]
 * Thread[pool-1-thread-2,5,main] notify() Thread[pool-1-thread-1,5,main]
 * notifyAll() Thread[pool-1-thread-1,5,main] Thread[pool-1-thread-2,5,main]
 * Thread[pool-1-thread-3,5,main] Thread[pool-1-thread-4,5,main]
 * Thread[pool-1-thread-5,5,main] notify() Thread[pool-1-thread-1,5,main]
 * notifyAll() Thread[pool-1-thread-1,5,main] Thread[pool-1-thread-5,5,main]
 * Thread[pool-1-thread-4,5,main] Thread[pool-1-thread-3,5,main]
 * Thread[pool-1-thread-2,5,main] notify() Thread[pool-1-thread-1,5,main]
 * notifyAll() Thread[pool-1-thread-1,5,main] Thread[pool-1-thread-2,5,main]
 * Thread[pool-1-thread-3,5,main] Thread[pool-1-thread-4,5,main]
 * Thread[pool-1-thread-5,5,main] Timer canceled Task2.blocker.prodAll()
 * Thread[pool-1-thread-6,5,main] Shutting down
 */// :~
